C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / LanguageSelector.vue
Displaying Raw • Download
meshchatx/src/frontend/components/LanguageSelector.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 5.32 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelativeTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrelative rounded-full p-1.5 sm:p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffapp.languageTa5d6ff'Ta5d6ff)Ta5d6ff"
@click.Te6edf3stop=Ta5d6ff"Ta5d6fftoggleDropdownTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fftranslateTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5 sm:w-6 sm:h-6Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<Teleport Te6edf3to=Ta5d6ff"Ta5d6ffbodyTa5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffisDropdownOpenTa5d6ff"
Te6edf3ref=Ta5d6ff"Ta5d6fflanguageDropdownTa5d6ff"
Te6edf3v-click-outside=Ta5d6ff"Ta5d6ffcloseDropdownTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fffixed w-48 bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-800 rounded-2xl shadow-xl z-[9999] overflow-x-hiddenTa5d6ff"
Te6edf3:style=Ta5d6ff"Ta5d6ffdropdownStyleTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6fflang in languagesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fflang.codeTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full px-4 py-2 text-left rounded-lg hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colors flex items-center justify-betweenTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{
Ta5d6ff'Ta5d6ffbg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400Ta5d6ff'Ta5d6ff:
currentLanguage === lang.code,
Ta5d6ff'Ta5d6fftext-gray-900 dark:text-zinc-100Ta5d6ff'Ta5d6ff: currentLanguage !== lang.code,
}Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectLanguage(lang.code)Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-mediumTa5d6ff"Tff7b72>{{ lang.name }}Tff7b72</span>
Tff7b72<MaterialDesignIcon Te6edf3v-if=Ta5d6ff"Ta5d6ffcurrentLanguage === lang.codeTa5d6ff" Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</Teleport>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "./MaterialDesignIcon.vue";
import { clampFloatingToViewport } from "../js/clampFloatingToViewport.js";
const localeModules = import.meta.glob("../locales/*.json", { eager: true });
const discoveredLanguages = Object.entries(localeModules)
.map(([filePath, mod]) => ({
code: filePath.match(/\/([^/]+)\.json$/)[1],
name: mod.default?._languageName || filePath.match(/\/([^/]+)\.json$/)[1],
}))
.sort((a, b) => {
if (a.code === "en") return -1;
if (b.code === "en") return 1;
return a.name.localeCompare(b.name);
});
export default {
name: "LanguageSelector",
components: {
MaterialDesignIcon,
},
directives: {
"click-outside": {
mounted(el, binding) {
el.clickOutsideEvent = function (event) {
if (!(el === event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
},
},
emits: ["language-change"],
data() {
return {
isDropdownOpen: false,
dropdownPosition: { top: 0, left: 0 },
dropdownMaxHeight: null,
};
},
computed: {
currentLanguage() {
return this.$i18n.locale;
},
languages() {
return discoveredLanguages;
},
dropdownStyle() {
const style = {
top: `${this.dropdownPosition.top}px`,
left: `${this.dropdownPosition.left}px`,
};
if (this.dropdownMaxHeight != null) {
style.maxHeight = `${this.dropdownMaxHeight}px`;
style.overflowY = "auto";
} else {
style.overflow = "hidden";
}
return style;
},
},
methods: {
toggleDropdown(event) {
this.isDropdownOpen = !this.isDropdownOpen;
if (this.isDropdownOpen) {
this.updateDropdownPosition(event);
}
},
updateDropdownPosition(event) {
const button = event.currentTarget;
const rect = button.getBoundingClientRect();
this.dropdownMaxHeight = null;
this.dropdownPosition = {
top: rect.bottom + 8,
left: Math.max(8, rect.right - 192),
};
this.$nextTick(() => {
const panel = this.$refs.languageDropdown;
if (!panel) return;
const pr = panel.getBoundingClientRect();
const { left, top, maxHeight } = clampFloatingToViewport(pr.left, pr.top, pr.width, pr.height);
this.dropdownPosition = { left, top };
this.dropdownMaxHeight = maxHeight;
});
},
closeDropdown() {
this.isDropdownOpen = false;
},
async selectLanguage(langCode) {
if (this.currentLanguage === langCode) {
this.closeDropdown();
return;
}
this.$emit("language-change", langCode);
this.closeDropdown();
},
},
};
Tff7b72</script>
Served by rngit 1.5.2 - Generated in 0.03s